package cn.kevinkun.Base64Util;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import com.google.appinventor.components.annotations.DesignerComponent;
import com.google.appinventor.components.annotations.SimpleEvent;
import com.google.appinventor.components.annotations.SimpleFunction;
import com.google.appinventor.components.annotations.SimpleObject;
import com.google.appinventor.components.annotations.UsesAssets;
import com.google.appinventor.components.annotations.UsesPermissions;
import com.google.appinventor.components.common.ComponentCategory;
import com.google.appinventor.components.runtime.AndroidNonvisibleComponent;
import com.google.appinventor.components.runtime.AndroidViewComponent;
import com.google.appinventor.components.runtime.Canvas;
import com.google.appinventor.components.runtime.Component;
import com.google.appinventor.components.runtime.ComponentContainer;
import com.google.appinventor.components.runtime.EventDispatcher;
import com.google.appinventor.components.runtime.Image;
import com.google.appinventor.components.runtime.ImageSprite;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.BitmapDrawable;
import android.util.Base64;
import android.view.View;
import android.widget.ImageView;

@DesignerComponent(version = 1, category = ComponentCategory.EXTENSION, description = "Util for base64.<br>if any issue/bug/suggestion, please mail to <a href=\"mailto:wangsk789@163.com\">wangsk789@163.com</a>", iconName = "images/extension.png", nonVisible = true, helpUrl = "")
@SimpleObject(external = true)
@UsesAssets(fileNames = "")
@UsesPermissions(permissionNames = "")
public class Base64Util extends AndroidNonvisibleComponent implements Component {
	Context context;

	public Base64Util(ComponentContainer container) {
		super(container.$form());
		this.context = container.$context();
	}

	/*
	 * Origin from TIMAI2.
	 * https://community.appinventor.mit.edu/t/base64-tools-four-little-ones/43433
	 */

	@SimpleFunction(description = "get base64 string of a Image component")
	public String ImageToBase64(Image image) {
		final ImageView imageView = (ImageView) (image).getView();
		Bitmap imageBitmap = ((BitmapDrawable) imageView.getDrawable()).getBitmap();
		String encodedString = bitMapToBase64(imageBitmap);
		return encodedString;

	}

	@SimpleFunction(description = "set Image component's image from base64 string")
	public void ImageFromBase64(Image image, String base64) {

		final ImageView imageView = (ImageView) (image).getView();
		byte[] decodedString = Base64.decode(base64, Base64.NO_WRAP);
		Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length);
		imageView.setImageBitmap(decodedByte);

	}

	@SimpleFunction(description = "get base64 string of Canvas (including background image and drawing on it)")
	public String CanvasToBase64(Canvas canvas) {

		Method method;
		try {
			method = canvas.getClass().getDeclaredMethod("getBitmap");
			Bitmap imageBitmap = (Bitmap) method.invoke(canvas);
			String encodedString = bitMapToBase64(imageBitmap);
			return encodedString;
		} catch (InvocationTargetException | IllegalAccessException | IllegalArgumentException | NoSuchMethodException
				| SecurityException e) {
			OnError("CanvasToBase64", e.getMessage());
			return "";
		}

	}

	@SimpleFunction(description = "get base64 string from a file. File located at ASD.")
	public String FileToBase64(String fileName) {
		if (fileName.startsWith("file://")) {
			fileName = fileName.substring(7);
		}
		if (!fileName.startsWith("/")) {
			fileName = context.getExternalFilesDir(null).getPath() + "/" + fileName;
		}
		File file = new File(fileName);
		FileInputStream inputFile;
		try {
			inputFile = new FileInputStream(file);
			byte[] buffer = new byte[inputFile.available()];
			inputFile.read(buffer);
			inputFile.close();
			String base64 = Base64.encodeToString(buffer, Base64.NO_WRAP);
			return base64;
		} catch (IOException e) {
			OnError("FileToBase64", e.getMessage());
			return "";
		}
	}

	@SimpleFunction(description = "directly save base64 string as a file. File located at ASD.")
	public void FileFromBase64(String fileName, String base64) {
		if (fileName.startsWith("file://")) {
			fileName = fileName.substring(7);
		}
		if (!fileName.startsWith("/")) {
			fileName = context.getExternalFilesDir(null).getPath() + "/" + fileName;
		}
		try {
			byte[] buffer = Base64.decode(base64, 2);
			FileOutputStream out;
			out = new FileOutputStream(fileName);
			out.write(buffer);
			out.close();

		} catch (IOException e) {
			OnError("FileFromBase64", e.getMessage());
		}
	}

	@SimpleFunction(description = "encode string to base64")
	public String StringToBase64(String string) {
		byte[] buffer = string.getBytes();
		String base64 = Base64.encodeToString(buffer, Base64.NO_WRAP);
		return base64;
	}

	@SimpleFunction(description = "decode base64 string")
	public String StringFromBase64(String base64) {
		byte[] buffer = Base64.decode(base64, Base64.NO_WRAP);
		String result = new String(buffer);
		return result;
	}

	@SuppressWarnings("deprecation")
	@SimpleFunction(description = "set ImageSprite's image from base64 string")
	public void ImageSpriteFromBase64(ImageSprite imageSprite, String base64) {
		try {
			byte[] bytes = Base64.decode(base64, Base64.DEFAULT);
			Bitmap myBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
			BitmapDrawable drawable = new BitmapDrawable(myBitmap);
			Field field = imageSprite.getClass().getDeclaredField("drawable");
			field.setAccessible(true);
			field.set(imageSprite, drawable);
			imageSprite.Width(imageSprite.Width());

		} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
			OnError("ImageSpriteFromBase64", e.getMessage());
		}
	}

	@SimpleFunction(description = "set the background image of visible component from base64 string")
	public void ComponentFromBase64(AndroidViewComponent component, String base64) {

		byte[] bytes = Base64.decode(base64, Base64.DEFAULT);
		Bitmap myBitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
		BitmapDrawable drawable = new BitmapDrawable(myBitmap);
		final View view = (View) (component).getView();
		view.setBackground(drawable);

	}

	@SimpleEvent
	public void OnError(String function, String error) {
		EventDispatcher.dispatchEvent(this, "OnError", function, error);
	}

	private String bitMapToBase64(Bitmap bitMap) {
		ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
		bitMap.compress(Bitmap.CompressFormat.PNG, 100, byteArrayOutputStream);
		byte[] byteArray = byteArrayOutputStream.toByteArray();
		return Base64.encodeToString(byteArray, Base64.NO_WRAP);
	}
}
